//////////////////////////////////////////////////////////////////////////////////
// Module: z80_wait_ctrl
// This module generates the Z80 WAIT signal (wait_out_n) used to control
// CPU wait states during I/O operations.
//////////////////////////////////////////////////////////////////////////////////
module z80_wait_ctrl #(
    parameter ADDR = 24'b000000
)(
    input  wire                  clk,               // Bus clock
    input  wire                  rst_n,             // System reset
    input  wire                  apb_psel_in,       // Peripheral select signal
    input  wire                  apb_penable_in,    // APB enable signal (ACCESS phase)
    input  wire                  apb_pwrite_in,     // APB write enable, 1 = write, 0 = read
    input  wire [31:0]           apb_paddr_in,      // APB address bus
    input  wire [31:0]           apb_pwdata_in,     // APB write data bus
    input  wire                  z80_iorq_n_in,
    input  wire                  z80_m1_in,
    output wire                  wait_out,
    output wire                  apb_pready_out
);

// Synchronizers for Z80 /RD and /WR control signals
reg [1:0] z80_iorq_sync;
assign iorq = ~z80_iorq_n_in & z80_m1_in;

always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        z80_iorq_sync  <= 2'b00;
    end
    else begin
        z80_iorq_sync  <= { z80_iorq_sync[0], iorq };
    end
end

reg wait_out_reg;
reg apb_pready_out_reg;
assign z80_iorq_pulse = (z80_iorq_sync == 2'b01);

always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        wait_out_reg <= 1'b0;
        apb_pready_out_reg <= 1'b1;
    end
    else begin
        // APB write.
        if (apb_psel_in && (apb_paddr_in[23:0] == ADDR)) begin
            if(~apb_penable_in) begin
                apb_pready_out_reg <= 1'b0;
            end
            else begin
                apb_pready_out_reg <= 1'b1;
                if(apb_pwrite_in) begin
                    wait_out_reg <= apb_pwdata_in[0];
                end
            end
        end
        else if(z80_iorq_pulse) begin
            wait_out_reg <= 1'b1;
		end
        // Idle.
        else begin
            apb_pready_out_reg <= 1'b1;
        end
    end
end

assign wait_out       = wait_out_reg;
assign apb_pready_out = apb_pready_out_reg;

endmodule